using System.Collections.Generic;
using Cinemachine;
using GameFramework.Battle.Core;
using Gameplay.PVE.Utils;
using UnityEngine;
using Yoozoo.Framework.Managers;
using Yoozoo.Managers;
using Yoozoo.Gameplay.RTS;
using Yoozoo.Gta.Common;

namespace Gameplay.PVE.TurnBase
{
    public class TurnBaseManager
    {
        private static TurnBaseManager instance;
        public static TurnBaseManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new TurnBaseManager();
                }
                return instance;
            }
        }

        private PveMap2D map;
        private PveResourceManager resourceManager;
        private PveCameraManager cameraManager;
        private TbsOperationManager operationManager;
        private PveFindPathManager findPathManager;
        private TimeManager timeManager;
        private Vector3 startPosition = new Vector3(100,0,100);
        private List<BattleUnit> units;
        private Dictionary<int, List<BattleUnit>> teamList;
        

        private TurnController turnController;
        private float endTimeStopTime;
        private int unitId = 0;

        private float teamUltraCurrentValue;

        public BattleUnit targetUnit;


        private bool isGoingToNext = false;
        private bool isJoining = false;
        
        public float TeamUltraCurrentValue
        {
            get { return teamUltraCurrentValue; }
            set
            {
                teamUltraCurrentValue = value;
                if (teamUltraCurrentValue > TeamUltraTotalValue)
                {
                    teamUltraCurrentValue = TeamUltraTotalValue;
                }

                if (teamUltraCurrentValue < 0)
                {
                    teamUltraCurrentValue = 0;
                }
                ResidentHandler.Inst.GetFunction("pveManager.TbsUpdateUltraPercent").Action(teamUltraCurrentValue/TeamUltraTotalValue);
            }
        }
        public float TeamUltraTotalValue = 100;

        private int currentWave = 0;
        
        public void Initialize()
        {
            PveUtils.pveUnitLayer =  LayerMask.NameToLayer(PveString.pveUnitLayer);
            units = new List<BattleUnit>();
            teamList = new Dictionary<int, List<BattleUnit>>();
            resourceManager = PveResourceManager.Instance;
            turnController = new TurnController();
            findPathManager = new PveFindPathManager();
            resourceManager.Initialize(() =>
            {
                findPathManager.Initialize();
                findPathManager.InitAstarMap(startPosition);
                InitCamera();
                TeamUltraCurrentValue = 0;
                map = new PveMap2D();
                map.Init(startPosition);
                //map.Create2DMap();
                map.Create2DMapProject();
                timeManager = TimeManager.Instance;
                operationManager = new TbsOperationManager();
                operationManager.Initialize();
                //cameraManager.SetTbsCameraPosition(startPosition);
                Start();
                
                PveEffectManager.Instance.Initialize();
            });
        }

        public void StartTbs(Vector3 position)
        {
            startPosition = position;
            this.units = new List<BattleUnit>();
            teamList = new Dictionary<int, List<BattleUnit>>();
            turnController = new TurnController();
            InitCamera();
            //cameraManager.SetTbsCameraPosition(startPosition);
            findPathManager = PveFindPathManager.Instance;
            resourceManager = PveResourceManager.Instance;
            timeManager = TimeManager.Instance;
            operationManager = new TbsOperationManager();
            operationManager.Initialize();
            Start();
        }
        
        public void Join(Vector3 position,float angleOffset,List<Entity.BattleUnit> units)
        {
            currentAngleOffset = angleOffset;
            this.startPosition = position;
            this.units = new List<BattleUnit>();
            teamList = new Dictionary<int, List<BattleUnit>>();
            turnController = new TurnController();

            isJoining = true;
            isGoingToNext = true;
            TeamUltraCurrentValue = 0;
            //map = new PveMap2D();
            //map.Init(startPosition);
            //map.Create2DMap();
            //map.Create2DMapProject();
            cameraManager = PveCameraManager.Instance;
            findPathManager = PveFindPathManager.Instance;
            resourceManager = PveResourceManager.Instance;
            timeManager = TimeManager.Instance;
            operationManager = new TbsOperationManager();
            operationManager.Initialize();
            //cameraManager.SetTbsCameraPosition(startPosition);
            List<GameObject> models = new List<GameObject>();
            for (int i = 0; i < units.Count; i++)
            {
                models.Add(units[i].GetModel());
            }
            StartJoin(models);
        }
        

        public void Update()
        {
            for (int i = 0; i < units.Count; i++)
            {
                units[i].Update();
            }
            turnController?.Update();
            timeManager?.Update();
            //cameraManager?.Update();
            operationManager?.Update();
            findPathManager?.Update();
            map?.Update();
            PveEffectManager.Instance.Update();
            if (endTimeStopTime != 0 && endTimeStopTime <= Time.unscaledTime)
            {
                TimeManager.logicTimeScale = 1;
                endTimeStopTime = 0;
            }

            if (nextFrameStop)
            {
                TimeManager.logicTimeScale = 0;
                nextFrameStop = false;
            }

            if (isGoingToNext)
            {
                bool isAllReady = true;
                var team = GetAllUnitByTeam(0);
                for (int i = 0; i < team.Count; i++)
                {
                    if (!team[i].Data.isInBirthPosition)
                    {
                        isAllReady = false;
                        break;
                    }
                }

                if (isAllReady)
                {
                    isGoingToNext = false;
                    if (isJoining)
                    {
                        isJoining = false;
                        //cameraManager.InitTbs();
                        //cameraManager.TransToTbsNormal(startPosition);
                    }
                    StartNext();
                }
            }
        }

        public void LateUpdate()
        {
            for (int i = 0; i < units.Count; i++)
            {
                units[i].LateUpdate();
            }
            //cameraManager?.Update();
        }
        
        public void Start()
        {
            for (int i = 0; i < 5; i++)
            {
                AddMyUnit(i);
            }
            for (int i = 0; i < 5; i++)
            {
                AddEnemyUnit(i);
            }
            int[] heros = new int[5];
            var list = GetAllUnitByTeam(0);
            for (int i = 0; i < list.Count; i++)
            {
                heros[i] = list[i].Data.index;
            }

            //AddObstacle();
            
            ResidentHandler.Inst.GetFunction("pveManager.StartTBS").Action(heros);
            turnController.Init();
        }

        private void StartJoin(List<GameObject> models)
        {
            for (int i = 0; i < models.Count; i++)
            {
                AddMyUnit(i,models[i]);
            }
            for (int i = 0; i < 5; i++)
            {
                AddEnemyUnit(i);
            }
            int[] heros = new int[models.Count];
            var list = GetAllUnitByTeam(0);
            for (int i = 0; i < list.Count; i++)
            {
                heros[i] = list[i].Data.config.id;
            }
            ResidentHandler.Inst.GetFunction("pveManager.StartTBS").Action(heros);
        }

        private void AddObstacle()
        {
            var obstacle = PveResourceManager.GetResource(PveResourceManager.EPveResType.Model, 100);
            if (obstacle != null)
            {
                obstacle.transform.SetParent(PveScene.pveRootTrans);
                obstacle.transform.position = new Vector3(102, 0, 100);
                obstacle.transform.localScale = new Vector3(0.2f,0.2f,0.2f);
                obstacle.transform.localEulerAngles = new Vector3(0,90,0);
                //findPathManager.AddObstacle(100,obstacle.transform.position,8,4);
            }
            obstacle = PveResourceManager.GetResource(PveResourceManager.EPveResType.Model, 100);
            if (obstacle != null)
            {
                obstacle.transform.SetParent(PveScene.pveRootTrans);
                obstacle.transform.position = new Vector3(98, 0, 100);
                obstacle.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
                obstacle.transform.localEulerAngles = new Vector3(0, 90, 0);
                //findPathManager.AddObstacle(101,obstacle.transform.position,8,4);
            }
        }
        
        private void AddMyUnit(int index,GameObject model = null)
        {
            var data = ClassManager.Get<UnitData>();
            data.id = unitId;
            data.index = index;
            data.team = 0;
            //data.damageRatio = 1;
            var unit = ClassManager.Get<BattleUnit>();
            unit.Initialize(data,model);
            data.Init(unit);
            if (model == null)
            {
                unit.Data.SetPosition(unit.Data.birthPosition);
                unit.Data.SetForward(unit.Data.birthForward);
                unit.Data.targetPosition = unit.Data.position;
            }
            else
            {
                unit.Data.SetPosition(model.transform.position);
                unit.Data.SetForward(model.transform.forward);
            }
            unit.Birth();
            units.Add(unit);
            int team = 0;
            if (!teamList.TryGetValue(team, out var list))
            {
                list = new List<BattleUnit>();
                teamList.Add(team,list);
            }
            list.Add(unit);
            unitId++;
            findPathManager.AddUnit(unit);
            if (!model)
            {
                unit.Transmit(ETransmitType.MoveToBirthPosition);
            }
        }

        private void AddEnemyUnit(int index)
        {
            var data = ClassManager.Get<UnitData>();
            data.id = unitId;
            data.index = index;
            data.team = 2;
            var unit = ClassManager.Get<BattleUnit>();
            unit.Initialize(data);
            data.Init(unit);
            unit.Birth();
            unit.Data.SetPosition(unit.Data.birthPosition);
            unit.Data.SetForward(unit.Data.birthForward);
            unit.Data.targetPosition = unit.Data.position;
            units.Add(unit);
            int team = 2;
            if (!teamList.TryGetValue(team, out var list))
            {
                list = new List<BattleUnit>();
                teamList.Add(team,list);
            }
            list.Add(unit);
            unitId++;
            findPathManager.AddUnit(unit);
        }

        public Vector4 GetMapBoundary()
        {
            var pos = startPosition;
            float offset = 6;
            return new Vector4(pos.x - offset,pos.x + offset,pos.z - offset,pos.z + offset);
        }
        
        private const float rowOffset = 1f;

        private float currentAngleOffset;
        
        /*private List<Vector3> myBirthPositions = new List<Vector3>
        {
            new Vector3(-2,0,0) *  rowOffset,
            new Vector3(-2,0,2f) *  rowOffset,
            new Vector3(-2,0,-2f) *  rowOffset,
            new Vector3(-4,0,0) *  rowOffset,
            new Vector3(-4,0,2f) *  rowOffset,
            new Vector3(-4,0,-2f) *  rowOffset,
        };
        
        private List<Vector3> enemyBirthPositions = new List<Vector3>
        {
            new Vector3(2,0,0) * rowOffset,
            new Vector3(2,0,2f) * rowOffset,
            new Vector3(2,0,-2f) * rowOffset,
            new Vector3(4,0,0) * rowOffset,
            new Vector3(4,0,2f) * rowOffset,
            new Vector3(4,0,-2f) * rowOffset,
        };*/
        
        private List<Vector3> myBirthPositions = new List<Vector3>
        {
            new Vector3(-3.2f,0,-1.31f) *  rowOffset,
            new Vector3(-3.9f,0,0.5f) *  rowOffset,
            new Vector3(-4.6f,0,-2.2f) *  rowOffset,
            new Vector3(-5,0,0.4f) *  rowOffset,
            new Vector3(-5,0,-4.16f) *  rowOffset,
            //new Vector3(2.1f,0,-1.39f) *  rowOffset,
        };
        
        private List<Vector3> enemyBirthPositions = new List<Vector3>
        {
            new Vector3(2.1f,0,-1.39f) *  rowOffset,
            new Vector3(3.21f,0,1.7f) * rowOffset,
            new Vector3(2.5f,0,-3.01f) * rowOffset,
            new Vector3(4.34f,0,0) * rowOffset,
            new Vector3(4.36f,0,-3.07f) * rowOffset,
            //new Vector3(2 - 100,0,2f - 100) * rowOffset,
        };

        public Vector3 GetBirthPosition(int index,int team)
        {
            if (team == 0)
            {
                return startPosition + Quaternion.AngleAxis(currentAngleOffset,Vector3.up) * myBirthPositions[index];
            }
            return startPosition + Quaternion.AngleAxis(currentAngleOffset,Vector3.up) * enemyBirthPositions[index];
        }

        public Vector3 GetBirthForward(int team)
        {
            Vector3 defaultForward = new Vector3(1 - team,0,0);
            return Quaternion.AngleAxis(currentAngleOffset,Vector3.up) * defaultForward;
        }

        private void InitCamera()
        {
            cameraManager = new PveCameraManager();
            cameraManager.Init();
            //cameraManager.InitTbs();
            //cameraManager.TbsNormal(startPosition);
        }

        public void AddTbsFocus(BattleUnit target)
        {
            //target.gameObject.SetLayerRecursively(PveUtils.blackScreenLayer);
            //cameraManager.AddTbsFocus(target);
        }
        
        public void RemoveTbsFocus(BattleUnit target)
        {
            //target.gameObject.SetLayerRecursively(PveUtils.pveUnitLayer);
            //cameraManager.RemoveTbsFocus(target);
        }

        public void UpdateTbsFocus(float time = 0.5f)
        {
            //cameraManager.UpdateTbsFocus(time);
        }

        public void Shake(float time)
        {
            //cameraManager.Shake(time);
        }

        public void ShowSkillCamera(Transform parent, int id)
        {
            //cameraManager.ShowSkillCamera(parent, id);
        }

        public void HideSkillCamera(int id)
        {
            //cameraManager.HideSkillCamera(id);
        }
        
        /*public void Attack()
        {
            for (int i = 0; i < units.Count; i++)
            {
                var unit = units[i];
                int team = unit.Data.GetTargetTeam();
                if (unit.Data.IsCanMove())
                {
                    var arg = ClassManager.Get<AttackTransmitArg>();
                    arg.id = 1001;
                    arg.target = GetTargetByTeam(team);
                    unit.Transmit(ETransmitType.Attack, arg);
                    unit.Data.restMoveCount--;
                    return;
                }
            }
        }*/

        public void UnitAttack(int id)
        {
            var unit = GetUnit(id);
            if (unit != null && unit.Data.IsCanMove() && !unit.Data.IsCasting)
            {
                unit.Transmit(ETransmitType.BeforeRound);
                if (!unit.Data.IsCanMove())
                {
                    return;
                }
                unit.Transmit(ETransmitType.Attack);
                unit.Data.RestMoveCount--;
            }
        }

        public void UnitSkill(int id)
        {
            var unit = GetUnit(id);
            if (unit != null && unit.Data.IsCanMove() && !unit.Data.IsCasting)
            {
                unit.Transmit(ETransmitType.BeforeRound);
                if (!unit.Data.IsCanMove())
                {
                    return;
                }
                unit.Transmit(ETransmitType.Skill);
                unit.Data.RestMoveCount--;
            }
        }

        public void UltraSkill()
        {
            /*if (TeamUltraCurrentValue == TeamUltraTotalValue)
            {
                var list = GetAllUnitByTeam(0);
                for (int i = 0; i < list.Count; i++)
                {
                    var unit = list[i];
                    if (!unit.Data.IsCasting)
                    {
                        var arg = ClassManager.Get<AttackTransmitArg>();
                        arg.id = 3001;
                        arg.targets = GetTargetsByTeam(unit.Data.GetTargetTeam(),true);
                        unit.Transmit(ETransmitType.Attack, arg);
                        TeamUltraCurrentValue = 0;
                        return;
                    }
                }
            }*/
        }

        public BattleUnit GetUnit(int id)
        {
            for (int i = 0; i < units.Count; i++)
            {
                var unit = units[i];
                if (unit.Data.id == id)
                {
                    return unit;
                }
            }
            Debug.LogError("找不到id = " + id);
            return null;
            
        }

        public List<BattleUnit> GetAllUnitByTeam(int team)
        {
            if (teamList.TryGetValue(team, out var list))
            {
                return list;
            }

            return null;
        }

        public List<BattleUnit> GetAllUnits()
        {
            return units;
        }

        public void UnitDead(int team,int id)
        {
            bool isDead = false;
            var list = GetAllUnitByTeam(team);
            for (int i = 0; i < list.Count; i++)
            {
                if (list[i].Data.id == id)
                {
                    isDead = true;
                    list.RemoveAt(i);
                    break;
                }
            }

            if (!isDead)
            {
                return;
            }
            operationManager.UnitDead(id);

            /*if (list.Count == 0)
            {
                var unit = GetUnit(id);
                cameraManager.TbsDeadShow(unit.Data.position);
            }*/
        }
        
        
        public void TimeStop(float stopTime)
        {
            endTimeStopTime = Time.unscaledTime + stopTime;
            nextFrameStop = true;
        }

        public void AddHit()
        {
            turnController.AddHit();
        }

        private int currentBlackScreenNum;
        
        public void AddBlackScreenSkill()
        {
            if (currentBlackScreenNum == 0)
            {
                PveEffectManager.Instance.ShowBlackScreen();
            }
            currentBlackScreenNum++;
        }

        public void RemoveBlackScreenSkill()
        {
            currentBlackScreenNum--;
            if (currentBlackScreenNum <= 0)
            {
                currentBlackScreenNum = 0;
                PveEffectManager.Instance.HideBlackScreen();
            }
        }

        public void SetTargetUnit(BattleUnit unit)
        {
            targetUnit = unit;
            UpdateCounterState();
        }

        public void UpdateCounterState()
        {
            bool[] counterList = new bool[6];
            if (targetUnit != null)
            {
                var teamList = GetAllUnitByTeam(0);
                for (int i = 0; i < teamList.Count; i++)
                {
                    if (teamList[i].Data.GetCounterType(targetUnit.Data.config.property) == ECounterType.Counter)
                    {
                        counterList[i] = true;
                    }
                }
            }
            ResidentHandler.Inst.GetFunction("pveManager.TbsUpdateCounterState").Action(counterList);
        }

        public int GetRound()
        {
            return turnController.GetRound();
        }


        public void ToNext()
        {
            currentWave++;
            startPosition += new Vector3(5,0,0);
            var myTeam = GetAllUnitByTeam(0);
            for (int i = 0; i < units.Count; i++)
            {
                var unit = units[i];
                if (unit.Data.team == 0 && unit.Data.isDead)
                {
                    unit.Transmit(ETransmitType.Reborn);
                    myTeam.Add(unit);
                }
            }
            
            for (int i = 0; i < myTeam.Count; i++)
            {
                myTeam[i].Transmit(ETransmitType.GoToNextStage);
            }
            
            TimeManager.Instance.DelayCall(300, () =>
            {
                //cameraManager.SetTbsCameraPosition(startPosition);
            });

            for (int i = 0; i < 5; i++)
            {
                AddEnemyUnit(i);
            }

            isGoingToNext = true;
        }

        private void StartNext()
        {
            turnController.Init();
        }

        public void MoveTo(Vector3 targetPosition)
        {
            units[0].Data.targetPosition = targetPosition;
        }
        
        private bool nextFrameStop = false;
    }
}